/*
 *  SoundPlayer.cpp
 *  SoundEngine
 *
 *  Created by handcn on 09-3-10.
 *  Copyright 2009 __MyCompanyName__. All rights reserved.
 *
 */

#include "SoundPlayer.h"
#import <AudioToolbox/AudioToolbox.h>
#import "Mutex.h"
//#include "OpenALSupport.h"

CSoundPlayer* CSoundPlayer::m_pThis = NULL;
pthread_mutex_t CSoundPlayer::m_MutexClass = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t CSoundPlayer::m_MutexSound = PTHREAD_MUTEX_INITIALIZER;

CSoundPlayer::CSoundPlayer() :
	m_i4ThreadState(SOUND_UPDATE_THREAD_RUNING),
	m_priority(31)
{

}

CSoundPlayer::~CSoundPlayer()
{
#ifdef kPlayerUseThread	
	if ( SOUND_UPDATE_THREAD_RUNING == m_i4ThreadState )
	{
		m_i4ThreadState = SOUND_UPDATE_THREAD_STOPING;
		int i4Times = 50;
		while ( SOUND_UPDATE_THREAD_STOPPED != m_i4ThreadState && i4Times-- > 0 )
		{
			usleep(10);
		}
		
		pthread_join(m_thread, NULL);
	}
#endif		//	kPlayerUseThread
	this->soundClear();
}

CSoundPlayer* CSoundPlayer::GetInstance()
{
	if (NULL == m_pThis)
	{
		printf("GetInstance()--Lock begin\n");
		CMutex MutexLock(&m_MutexClass);
		printf("GetInstance()--Lock end\n");
		
		if (NULL == m_pThis)
		{
			CSoundPlayer* pPlayer = new CSoundPlayer();
			if ( pPlayer )
			{
				pPlayer->InitPlayer();
				pPlayer->m_i4CountRef = 0;
			}
			
			m_pThis = pPlayer;
		}
	}
	
	m_pThis->m_i4CountRef++;
	
	return m_pThis;
}

int CSoundPlayer::InitPlayer()
{
	soundClear();
	
	if ( !this->_InitAudioSession() )
	{
		printf("Error Initialize Audio Session!\n");
		assert(false);
		return 0;
	}
	
	if ( !this->_InitOpenAL() )
	{
		printf("Error Initialize OpenAL!\n");
		assert(false);
		return 0;
	}

#ifdef kPlayerUseThread	
	m_i4ThreadState = SOUND_UPDATE_THREAD_RUNING;
	
	m_thread_hdl = pthread_create( &m_thread,
								  NULL,
								  UpdateThread,
								  (void *)this );	
	//msSleep( 100 );
#endif
	
	return 1;
}

int CSoundPlayer::_InitAudioSession()
{
	int i4Ret = 0;
	//	setup our audio session
	OSStatus result = AudioSessionInitialize(NULL, NULL, AudioSessionISP, this);
	if (result) printf("Error initializing audio session! %d\n", result);
	else {
		UInt32 category = kAudioSessionCategory_AmbientSound;
		result = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(category), &category);
		if (result) printf("Error setting audio session category! %d\n", result);
		else {
			result = AudioSessionSetActive(true);
			if (result) printf("Error setting audio session active! %d\n", result);
			else i4Ret = 1;
		}
	}
	return i4Ret;
}

int CSoundPlayer::_InitOpenAL()
{
	int			i4Ret = 0;
	ALenum		error;
	ALCdevice	*device = NULL;
	ALCcontext	*context = NULL;
	
	device = alcOpenDevice(NULL);
	assert(NULL != device);
	if ( NULL != device )
	{
		context = alcCreateContext(device, 0);
		assert(NULL != context);
		if (NULL != context)
		{
			alcMakeContextCurrent(context);
			
			alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
			
			ALfloat listenerPos[]={0.0,0.0,0.0};
			ALfloat listenerVel[]={0.0,0.0,0.0};
			ALfloat	listenerOri[]={0.0,0.0,-1.0, 0.0,1.0,0.0};	// Listener facing into the screen
			
			alListenerfv(AL_POSITION, listenerPos);
			if ((error = alGetError()) != AL_NO_ERROR)
			{
				printf("Error setting listener position: %x\n", error);
			}
			
			alListenerfv(AL_VELOCITY, listenerVel);
			if ((error = alGetError()) != AL_NO_ERROR)
			{
				printf("Error setting listener velocity: %x\n", error);				
			}
			
			alListenerfv(AL_ORIENTATION, listenerOri);
			if ((error = alGetError()) != AL_NO_ERROR)
			{
				printf("Error setting listener orientation: %x\n", error);				
			}
			alListenerf(AL_GAIN, 1.0f);
			if ((error = alGetError()) != AL_NO_ERROR)
			{
				printf("Error setting listener gain: %x\n", error);				
			}
			
			i4Ret = 1;
		}
		else {
			error = alGetError();
			printf("Error Create Context! %x\n", error);
			assert(false);
		}
		
	}
	else 
	{
		error = alGetError();
		printf("Error Open Device! %x\n", error);
		assert(false);
	}
	return i4Ret;
}

//	中断产生
void CSoundPlayer::_InterruptArise()
{
#ifdef kPlayerUseThread	
	printf("_InterruptArise()--Lock begin\n");
	CMutex mutex(&m_MutexSound);
	printf("_InterruptArise()--Lock end\n");
#endif	
	mapStrToSound::iterator it = m_mapStrToSound.begin();
	mapStrToSound::iterator itEnd = m_mapStrToSound.end();
	while (it != itEnd) {
		//	记录音乐当前播放状态
		it->second->State();
		it->second->Release();
	}
	
	ALCdevice	*device = NULL;
	ALCcontext	*context = NULL;
	
	context = alcGetCurrentContext();
	device = alcGetContextsDevice(context);
	alcDestroyContext(context);
	alcCloseDevice(device);
}

//	中断恢复
void CSoundPlayer::_InterruptLeave()
{
	UInt32 category = kAudioSessionCategory_AmbientSound;
	OSStatus result = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(category), &category);
	if (result) printf("Error setting audio session category! %d\n", result);
	else {
		result = AudioSessionSetActive(true);
		if (result) printf("Error setting audio session active! %d\n", result);	
	}
#ifdef kPlayerUseThread	
	printf("_InterruptLeave()--Lock begin\n");
	CMutex mutex(&m_MutexSound);
	printf("_InterruptLeave()--Lock end\n");
#endif	
	this->_InitOpenAL();
	
	mapStrToSound::iterator it = m_mapStrToSound.begin();
	mapStrToSound::iterator itEnd = m_mapStrToSound.end();
	while (it != itEnd) {
		int state = it->second->LastState();
		bool bLooping = it->second->IsLooping();
		it->second->Init(it->first.c_str());
		if (AL_PLAYING == state)
		{
			it->second->Play(bLooping);
		}
	}
}


void CSoundPlayer::AudioSessionISP(void * inClientData, UInt32 inInterruptionState)
{
	CSoundPlayer *pPlayer = (CSoundPlayer*)inClientData;
	if ( kAudioSessionBeginInterruption == inInterruptionState )
	{	//	开始中断通知
		pPlayer->_InterruptArise();
	}
	else if ( kAudioSessionEndInterruption == inInterruptionState )
	{	//	结束中断通知
		pPlayer->_InterruptLeave();
	}
}

void* CSoundPlayer::UpdateThread( void *pVoid )
{
	CSoundPlayer *pPlayer = (CSoundPlayer*)pVoid;
	bool bRuning = true;
	
	struct sched_param param;
	
	param.sched_priority = pPlayer->m_priority;
	pthread_setschedparam( pPlayer->m_thread, SCHED_RR, &param );
	
	while (bRuning) {
		switch (pPlayer->m_i4ThreadState) {
			case SOUND_UPDATE_THREAD_RUNING:
			{	
				pPlayer->Update(0.0f);
				
				break;
			}
			case SOUND_UPDATE_THREAD_STOPING:
			{
				bRuning = false;
				break;
			}
			default:
				break;
		}

		usleep(50);
	}
	pPlayer->m_i4ThreadState = SOUND_UPDATE_THREAD_STOPPED;
	
	return NULL;
}